using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Drawing;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;

namespace JohnCummins.Windows.Screensaver
{
    struct Vertex
    {
        float x, y, z;
        int color;

        public Vertex(float _x, float _y, float _z, int _color)
        {
            x = _x; y = _y; z = _z;
            color = _color;
        }

        public static readonly VertexFormats FVF_Flags = VertexFormats.Position | VertexFormats.Diffuse;
    };

    class NetworkScreensaver : Screensaver
    {
        Device _device;
        Microsoft.DirectX.Direct3D.Font _font;
        float _angle = 0f;

        private VertexBuffer _vertexBuffer         = null;
        private VertexBuffer _vertexBuffer_indexed = null;
        private IndexBuffer  _indexBuffer          = null;

        private Entity _entity;

        public NetworkScreensaver() : base(FullscreenMode.MultipleWindows)
        {
            Initialize += new EventHandler(NetworkScreensaver_Initialize);
            Update += new EventHandler(NetworkScreensaver_Update);
            Exit += new EventHandler(NetworkScreensaver_Exit); 
        }

        void NetworkScreensaver_Initialize(object sender, EventArgs e)
        {
            PresentParameters pp = new PresentParameters();

            if (this.Mode != ScreensaverMode.Normal) 
                pp.Windowed = true;
            else 
            {
                pp.Windowed = false;
                pp.BackBufferCount = 1;
                pp.BackBufferWidth = Manager.Adapters[Window0.DeviceIndex].CurrentDisplayMode.Width;
                pp.BackBufferHeight = Manager.Adapters[Window0.DeviceIndex].CurrentDisplayMode.Height;
                pp.BackBufferFormat = Manager.Adapters[Window0.DeviceIndex].CurrentDisplayMode.Format;
            }

            pp.SwapEffect = SwapEffect.Flip;

            _device = new Device(Window0.DeviceIndex, DeviceType.Hardware, Window0.Handle, CreateFlags.HardwareVertexProcessing, pp);

            Window0.DoubleBuffer = false;
            _font = new Microsoft.DirectX.Direct3D.Font(_device, System.Drawing.SystemFonts.DefaultFont);

            _entity = new Entity();

            SetUpCamera();
            VertexDeclaration();
        }

        void NetworkScreensaver_Update(object sender, EventArgs e)
        {
            _angle += .05f;

            _device.Clear(ClearFlags.Target, System.Drawing.Color.Black, 0, 0);

            // construct the scene
            _device.BeginScene();
            _device.VertexFormat = CustomVertex.PositionColored.Format;
            _device.Transform.World = Matrix.Translation(-5, -10 * 1 / 3, 0) * Matrix.RotationAxis(new Vector3(_angle * 4, _angle * 2, _angle * 3), _angle);
            _device.DrawUserPrimitives(PrimitiveType.TriangleList, 1, _vertices);
            _device.RenderState.Lighting = false;
            
            _font.DrawText(null, string.Format("fps: {0}", this.AchievedFramerate), 0, 0, System.Drawing.Color.Blue.ToArgb());            
            _device.EndScene();

            // present the constructed scene
            _device.Present();
        }

        void NetworkScreensaver_Exit(object sender, EventArgs e)
        {
            _device.Dispose();
        }

        private void SetUpCamera()
        {
            _device.Transform.Projection = Matrix.PerspectiveFovLH((float)Math.PI / 4, _device.PresentationParameters.BackBufferWidth / _device.PresentationParameters.BackBufferHeight, 1f, 50f);
            _device.Transform.View = Matrix.LookAtLH(new Vector3(0, 0, -30), new Vector3(0, 0, 0), new Vector3(0, 1, 0));
            _device.RenderState.Lighting = false;
            _device.RenderState.CullMode = Cull.None;
        }

        private void VertexDeclaration()
        {
            _vertexBuffer = new VertexBuffer(typeof(Vertex),
                                 _entity.Vertices.Length, _device,
                                 Usage.Dynamic | Usage.WriteOnly,
                                 Vertex.FVF_Flags,
                                 Pool.Default);

            GraphicsStream gStream = vertexBuffer.Lock(0, 0, LockFlags.None);


            // Now, copy the vertex data into the vertex buffer
            gStream.Write(_entity.Vertices);

            _vertexBuffer.Unlock();

            //
            // Create a vertex buffer, which contains indexed geometry...
            //

            _vertexBuffer_indexed = new VertexBuffer(typeof(Vertex),
                                             _entity.Vertices.Length, _device,
                                             Usage.Dynamic | Usage.WriteOnly,
                                             Vertex.FVF_Flags,
                                             Pool.Default);

            gStream = _vertexBuffer_indexed.Lock(0, 0, LockFlags.None);

            // Now, copy the vertex data into the vertex buffer
            gStream.Write(_g_cubeVertices_indexed);

            vertexBuffer_indexed.Unlock();

            //
            // Create an index buffer to use with our indexed vertex buffer...
            //

            _indexBuffer = new IndexBuffer(typeof(int), 24, _device,
                                           Usage.WriteOnly, Pool.Default);

            gStream = _indexBuffer.Lock(0, 0, LockFlags.None);

            // Now, copy the indices data into the index buffer
            gStream.Write(_g_cubeIndices);

            _indexBuffer.Unlock();
        }

        /// <summary>
        /// This method is dedicated completely to rendering our 3D scene and is
        /// is called by the OnPaint() event-handler.
        /// </summary>
        private void Render()
        {
            _device.Clear(ClearFlags.Target | ClearFlags.ZBuffer,
                             Color.FromArgb(255, 0, 0, 0), 1.0f, 0);

            _device.BeginScene();

            _device.Transform.World =
                Matrix.RotationYawPitchRoll(Geometry.DegreeToRadian(spinX),
                Geometry.DegreeToRadian(spinY), 0.0f) *
                Matrix.Translation(0.0f, 0.0f, 5.0f);

            if (useIndexedGeometry == true)
            {
                _device.VertexFormat = Vertex.FVF_Flags;
                _device.SetStreamSource(0, _vertexBuffer_indexed, 0);
                _device.Indices = _indexBuffer;

                _device.DrawIndexedPrimitives(PrimitiveType.TriangleStrip, 0, 0, 8, 0, 2);
                _device.DrawIndexedPrimitives(PrimitiveType.TriangleStrip, 0, 0, 8, 4, 2);
                _device.DrawIndexedPrimitives(PrimitiveType.TriangleStrip, 0, 0, 8, 8, 2);
                _device.DrawIndexedPrimitives(PrimitiveType.TriangleStrip, 0, 0, 8, 12, 2);
                _device.DrawIndexedPrimitives(PrimitiveType.TriangleStrip, 0, 0, 8, 16, 2);
                _device.DrawIndexedPrimitives(PrimitiveType.TriangleStrip, 0, 0, 8, 20, 2);
            }
            else
            {
                _device.VertexFormat = Vertex.FVF_Flags;
                _device.SetStreamSource(0, _vertexBuffer, 0);

                _device.DrawPrimitives(PrimitiveType.TriangleStrip, 0, 2);
                _device.DrawPrimitives(PrimitiveType.TriangleStrip, 4, 2);
                _device.DrawPrimitives(PrimitiveType.TriangleStrip, 8, 2);
                _device.DrawPrimitives(PrimitiveType.TriangleStrip, 12, 2);
                _device.DrawPrimitives(PrimitiveType.TriangleStrip, 16, 2);
                _device.DrawPrimitives(PrimitiveType.TriangleStrip, 20, 2);
            }

            _device.EndScene();

            _device.Present();
        }

    }
}
